
import {
  getRandomNumberArr,
  b64_to_u8arr,
  u8arr_to_b64,
  encoder,
  decoder
} from './lib'

class AESCryptoTool {
  #b64key: string
  #iv: Uint8Array
  key: CryptoKey
  constructor(key?: string, iv?: Uint8Array) {
    this.#iv = iv || getRandomNumberArr()
    this.#b64key = key
  }
  async Init(): Promise<null | CryptoKey> {
    if (!this.#b64key) return null
    const result = await window.crypto.subtle.importKey('raw', b64_to_u8arr(this.#b64key),
      {
        name: "AES-CBC",
        length: 128
      },
      true,
      ["encrypt", "decrypt"]
    )
    this.key = result
    return result
  }
  /**
   * 生成aes key
   * 算法AES-GCM,长度128,可导出
   * @returns 
   */
  async generateKey() {
    let key = await window.crypto.subtle.generateKey(
      {
        name: "AES-CBC",
        length: 128
      },
      true,
      ["encrypt", "decrypt"]
    )
    this.key = key
    return key
  }
  /**
   * 导出aeskey
   * @returns 
   */
  async exoportKey() {
    const ab = await window.crypto.subtle.exportKey('raw', this.key)
    const keyAsString = u8arr_to_b64(new Uint8Array(ab))
    const keyAsBase64 = window.btoa(keyAsString)
    return keyAsBase64
  }
  async encrypto(content: Uint8Array): Promise<ArrayBuffer> {
    return window.crypto.subtle.encrypt(
      {
        name: "AES-CBC",
        iv: this.#iv
      },
      this.key,
      content
    )
  }
  async decrypto(ciphertext: Uint8Array) {
    return await window.crypto.subtle.decrypt(
      {
        name: "AES-CBC",
        iv: this.#iv
      },
      this.key,
      ciphertext
    )
  }
}

class AESCryptoToolExt extends AESCryptoTool {
  /**
   * 将字符串加密
   * @returns 密文
   */
  async encryptoByString(str: string): Promise<string> {

    const u8arr = encoder.encode(str)
    const ab = await this.encrypto(u8arr)
    const b64 = u8arr_to_b64(new Uint8Array(ab))
    return b64
  }
  /**
   * 将对象加密
   * @param {any} obj 
   */
  encryptoByObj(obj: any) {
    obj ||= {}
    const str = JSON.stringify(obj)
    return this.encryptoByString(str)
  }
  /**
   * 将字符串解密
   */
  async decryptoByString(b64str: string) {
    const ab = await this.decrypto(b64_to_u8arr(b64str))
    const abString = decoder.decode(ab)
    return abString
  }
}
async function aesGenerateKeyTest() {
  console.log('=======================================================>');
  console.log('开始初始化加密工具:生成key测试');
  const aesTool = new AESCryptoToolExt()
  const originalData = { name: '家铭' }
  const strtext = JSON.stringify(originalData)
  console.log('原对象', strtext, originalData);
  await aesTool.generateKey()
  console.log(await aesTool.exoportKey());
  console.log('开始加密对象....', originalData);
  const ObjEcryptoResult = await aesTool.encryptoByObj(originalData)
  console.log('对象加密结果:', ObjEcryptoResult);
  console.log('开始加密字符串....', strtext);
  const stringEcryptoResult = await aesTool.encryptoByString(strtext)
  console.log('字符串加密结果', stringEcryptoResult);
  console.log('开始解密对象....');
  const objDecryptoResult = await aesTool.decryptoByString(ObjEcryptoResult)
  console.log('对象密文解密结果:', objDecryptoResult);
  const strDecryptoResult = await aesTool.decryptoByString(stringEcryptoResult)
  console.log('字符串密文解密结果:', strDecryptoResult);

  console.log('<=======================================================');
}
async function aesImportKeyTest() {
  console.log('=======================================================>');
  console.log('开始初始化加密工具:导入外部key测试');
  const tempKey = 'jm/7kedOX/Dxn8XotVG27Q=='
  const aesTool = new AESCryptoToolExt(tempKey)
  const originalData = { name: '家铭' }
  const strtext = JSON.stringify(originalData)
  console.log('原对象', strtext, originalData);
  await aesTool.Init()
  console.log('开始加密对象....', originalData);
  const ObjEcryptoResult = await aesTool.encryptoByObj(originalData)
  console.log('对象加密结果:', ObjEcryptoResult);
  console.log('开始加密字符串....', strtext);
  const stringEcryptoResult = await aesTool.encryptoByString(strtext)
  console.log('字符串加密结果', stringEcryptoResult);
  console.log('开始解密对象....');
  const objDecryptoResult = await aesTool.decryptoByString(ObjEcryptoResult)
  console.log('对象密文解密结果:', objDecryptoResult);
  const strDecryptoResult = await aesTool.decryptoByString(stringEcryptoResult)
  console.log('字符串密文解密结果:', strDecryptoResult);

  console.log('<=======================================================');
}

export { AESCryptoTool, AESCryptoToolExt, aesGenerateKeyTest, aesImportKeyTest }